/**
 * 如何定义Model
 * sequelize基础配置文件
 */
const Sequelize = require('sequelize');
//随机生成id序列，自定义的
const guid = require('../src/main/utils/guid');
//系统引用的
const uuid = require('uuid');

const config = require("./config");

const Op = Sequelize.Op;
const operatorsAliases = {
  $eq: Op.eq,
  $ne: Op.ne,
  $gte: Op.gte,
  $gt: Op.gt,
  $lte: Op.lte,
  $lt: Op.lt,
  $not: Op.not,
  $in: Op.in,
  $notIn: Op.notIn,
  $is: Op.is,
  $like: Op.like,
  $notLike: Op.notLike,
  $iLike: Op.iLike,
  $notILike: Op.notILike,
  $regexp: Op.regexp,
  $notRegexp: Op.notRegexp,
  $iRegexp: Op.iRegexp,
  $notIRegexp: Op.notIRegexp,
  $between: Op.between,
  $notBetween: Op.notBetween,
  $overlap: Op.overlap,
  $contains: Op.contains,
  $contained: Op.contained,
  $adjacent: Op.adjacent,
  $strictLeft: Op.strictLeft,
  $strictRight: Op.strictRight,
  $noExtendRight: Op.noExtendRight,
  $noExtendLeft: Op.noExtendLeft,
  $and: Op.and,
  $or: Op.or,
  $any: Op.any,
  $all: Op.all,
  $values: Op.values,
  $col: Op.col
};
const sequelize = new Sequelize(
    config.database,
    config.username,
    config.password,
    {
      host:config.host,
      port: config.port,
      dialect:config.dialect,
      operatorsAliases: true,
      pool: {
        max: 5,
        min: 2,
        acquire: 30000,
        idle: 10000
      },
      // 'define': {
      //   // 字段以下划线（_）来分割（默认是驼峰命名风格）,驼峰和下划线之间的互转 ,单独设置还是全局设置都不管用，硬要在每个字段的定义里面加field来表示
      //   'underscored': true
      // },
      dialectOptions: {
        multipleStatements: true //允许多条语句执行
      },

      // logging:function (sql) {//自定义日志形式
      //   console.log(sql)
      //   console.log("=-=========================")
      // }
      // SQLite only
      // storage: 'path/to/database.sqlite'
    }
);

//id生成器
function generateId() {
  return uuid.v4();
}

console.log('init sequelize database is '+config.database+'...');
//通用的模型配置
const ID_TYPE = Sequelize.STRING(64);

function defineModel(name, attributes) {
  var attrs = {};
  //处理属性
  for (let key in attributes) {
    let value = attributes[key];
    if (typeof value === 'object' && value['type']) {
      value.allowNull = value.allowNull || false;//属性不能为null，除非显示的指定
      attrs[key] = value;
    } else {
      attrs[key] = {
        type: value,
        allowNull: false
      };
    }
  }
  //几个自定义的字段
  attrs.id = {
    type: ID_TYPE,
    primaryKey: true
  };
  attrs.createdTime = {
    type: Sequelize.BIGINT,
    allowNull: false,
    "field":"created_time"
  };
  attrs.updatedTime = {
    type: Sequelize.BIGINT,
    allowNull: false,
    field:"updated_time"

  };
  attrs.version = {
    type: Sequelize.BIGINT,
    allowNull: false
  };
  //打印表信息：
  // console.debug('model defined for table: ' + name + '\n' + JSON.stringify(attrs, function (k, v) {
  //   if (k === 'type') {
  //     for (let key in Sequelize) {
  //       if (key === 'ABSTRACT' || key === 'NUMBER') {
  //         continue;
  //       }
  //       let dbType = Sequelize[key];
  //       if(dbType == undefined){
  //         continue;
  //       }
  //       if (typeof dbType === 'function') {
  //         try{
  //           if (v instanceof dbType) {
  //             if (v._length) {
  //               return `${dbType.key}(${v._length})`;
  //             }
  //             return dbType.key;
  //           }
  //         }catch (e) {
  //           // console.log(e);
  //         }
  //
  //         if (v === dbType) {
  //           return dbType.key;
  //         }
  //       }
  //     }
  //   }
  //   return v;
  // }, '  '));
  
  return sequelize.define(name, attrs, {
    /**如果为 true 则表的名称和 model 相同，即 user
     * 为 false MySQL 创建的表名称会是复数 users
     * 如果指定的表名称本就是复数形式则不变
     */
    freezeTableName: false,
    // underscored: true,//驼峰和下划线之间的互转 ,单独设置还是全局设置都不管用，硬要在每个字段的定义里面加field来表示
    tableName: config.prefix+name, //表名
    timestamps: false, // 不启动自动化增加updateAt和createAt两个字段，我们自己增加了
    hooks: {
      beforeValidate: function (obj) { // 在使用model映射 生效之前
        console.log("before validate!")
        let now = Date.now();
        if (obj.isNewRecord) {  // 根据这个字段来判定是否是新的记录
          if (!obj.id) {
            obj.id = generateId();//guid.guid();
          }
          obj.createdTime = now;
          obj.updatedTime = now;
          obj.version = 0;
          console.log('will create entity...' + obj.version+">>"+obj.toString());
        }
      },
      validationFailed:function(obj,opts,err){
        console.log("validate failded!"+err)
      },
      beforeUpdate:function(obj){
        console.log("before update!")
        if(!obj.isNewRecord){ //下面这个在教程里面放在beforeValidate，是不能更改updatedTime和version的
            obj.updatedTime = Date.now();
            obj.version++;
            console.log('will update entity...' + obj.version+">>"+obj.toString());
        }
      }
    }
  });
}

function syncModel2db(option) {
  if(!option){
     option = {
       force: true       //在非生成环境下每次都是重新建立表的，是因为为了表修改后同步到数据库
     }
  }
  console.log("======创建数据库了！============")
  if (process.env.NODE_ENV !== 'production') { //only allow create ddl in non-production environment:
    /** 创建表
     * User.sync() 会创建表并且返回一个 Promise 对象
     * 如果 force = true 则会把存在的表（如果 users 表已存在）先销毁再创建表
     * 默认情况下 forse = false
     */
    return sequelize.sync(option);
    //直接返回一个promise对象，后续的操作他们自己去完成 ,一开始我也是想着使用这样的callback来完成数据的加载，但是这样想想还是不友好！
    /**
    .then(()=>{
      console.log('sync done,db inited');

    })
    .then(()=>{
      console.log("第二个then")
      if(callback && (typeof callback === 'function') ){
        console.log("callback执行了！")
        callback();
      }
    })
    .catch((e) =>{
      console.log(e)
      // console.log(`failed:${e}`);
    })
    // .finally(() => {
    //   process.exit(0);
    // });
     */
  } else {
    throw new Error('Cannot sync() when NODE_ENV is set to \'production\'.');
  }
}

const TYPES = ['STRING', 'INTEGER', 'BIGINT', 'TEXT', 'DOUBLE', 'DATEONLY', 'BOOLEAN',
  'ENUM','FLOAT','DATE','DATETIME','TINYTEXT','REAL','DECIMAL','BOOLEAN','JSON','UUID'];
var exp = {
  defineModel: defineModel,
  sync:syncModel2db,
  proto_query:statementQuery
};

function statementQuery(sql,options) {
  if(!options){
    options = {}
  }
  return sequelize.query(sql,options);
}


for (let type of TYPES) {
  exp[type] = Sequelize[type];
}
exp.ID = ID_TYPE;
exp.generateId = generateId;

module.exports = exp;